package com.yvan.dsf;

import com.alibaba.dubbo.rpc.RpcContext;
import com.yvan.dsf.tracing.api.Annotation;
import com.yvan.dsf.tracing.api.Endpoint;
import com.yvan.dsf.tracing.api.Span;
import com.yvan.dsf.tracing.client.ContextHolder;
import com.yvan.dsf.tracing.client.DstConstants;
import com.yvan.dsf.tracing.client.dubbo.TracingFilter;
import com.yvan.dsf.tracing.client.util.GUId;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.management.ManagementFactory;

/**
 * Created by luoyifan on 2018/2/28.
 */
public class DsfHeadeFilter implements Filter {

    private static final String PID = ManagementFactory.getRuntimeMXBean().getName();
    private final String applicationName;

    public DsfHeadeFilter(String applicationName) {
        this.applicationName = applicationName;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
            chain.doFilter(request, response);
            return;
        }

        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        String thisUrl = httpRequest.getRequestURI();
        if (thisUrl.startsWith("/static") ||
                thisUrl.endsWith(".css") || thisUrl.endsWith(".jpg") || thisUrl.endsWith(".jpeg") ||
                thisUrl.endsWith(".html") || thisUrl.endsWith(".js") || thisUrl.endsWith(".png") ||
                thisUrl.endsWith(".gif") || thisUrl.endsWith(".map")|| thisUrl.endsWith(".ico")) {
            chain.doFilter(request, response);
            return;
        }

        String traceId = GUId.singleton().nextId();
        ContextHolder.setTraceId(traceId);
        RpcContext rpcContext = RpcContext.getContext();

        try {

            Span span = new Span();
            span.setId(GUId.singleton().nextId());
            span.setTraceId(traceId);
            span.setParentId(null);
            span.setServiceName(httpRequest.getRequestURI());
            span.setName(this.applicationName);
            ContextHolder.setSpan(span);

            Annotation annotation = new Annotation();
            annotation.setValue(Annotation.CLIENT_SEND);
            annotation.setEndpoint(createEndpoint(httpRequest));
            annotation.setTimestamp(System.currentTimeMillis());
            span.addAnnotation(annotation);

            rpcContext.setAttachment(DstConstants.DST_IS_SAMPLE, "true");
            rpcContext.setAttachment(DstConstants.DST_TRACE_ID, traceId);
            rpcContext.setAttachment(DstConstants.DST_SPAN_ID, span.getId());
            rpcContext.setAttachment(DstConstants.DST_PARENT_SPAN_ID, null);

            ((HttpServletResponse) response).setHeader("YVAN_HOST", PID);
            ((HttpServletResponse) response).setHeader("TRACE_ID", traceId);
            chain.doFilter(httpRequest, httpResponse);

        } finally {
            Span span = ContextHolder.getSpan();
            if (span != null) {
                Endpoint endpoint = createEndpoint(httpRequest);
                Annotation annotation = new Annotation();
                annotation.setValue(Annotation.CLIENT_RECEIVE);
                annotation.setEndpoint(endpoint);
                annotation.setTimestamp(System.currentTimeMillis());
                span.addAnnotation(annotation);
            }
            Span spanSend = ContextHolder.popSpan();
            if (spanSend != null) {
                TracingFilter.tracer.syncTransfer.syncSend(span);
            }
            ContextHolder.removeAll();
        }
    }

    private Endpoint createEndpoint(HttpServletRequest httpRequest) {
        Endpoint endpoint = new Endpoint();
        endpoint.setServiceName(this.applicationName);
        endpoint.setIpv4(httpRequest.getLocalAddr());
        endpoint.setPort(httpRequest.getLocalPort());
        return endpoint;
    }

    @Override
    public void destroy() {

    }
}
